/*
 * Created by SharpDevelop.
 * User: David
 * Date: 2006-12-20
 * Time: 04:02
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Windows.Forms;
using System.Diagnostics;

using Labb4.Pooman.Cards;
using Labb4.Rules;

namespace Labb4.Pooman.Players
{
	/// <summary>
	/// An ai for playing phase 1, with getting cards that are concecutive in suit as agenda
	/// </summary>
	public class AiGameStatePhase1Suit : AiGameState
	{
		#region members
		
		/// <summary>
		/// The minimum number of cards to win before starting to look for connections among
		/// the cards on the table in reference to the cards allready taken
		/// </summary>
		private readonly int		m_MinimumCardsWon;
		/// <summary>
		/// The calculated sacrifice minimum to draw a chance card from the deck
		/// </summary>
		private readonly int		m_ChanceLimit;
		
		#endregion
		
		#region constructors
		
		/// <summary>
		/// Constructor, initializes members
		/// </summary>
		public AiGameStatePhase1Suit() :
			base()
		{
			m_MinimumCardsWon = 6;
			m_ChanceLimit = -4;
		}
		
		#endregion
		
		#region methods
		
		/// <summary>
		/// Determines the numerical distance between a Card and the nearest card
		/// in a CardPile.
		/// </summary>
		/// <param name="card">The Card to evaluate</param>
		/// <param name="cards">The CardPile to meassure to</param>
		/// <returns></returns>
		public int Distance(Card card, CardPile cards)
		{
			int distance = 13;
			
			foreach(Card it in cards)
			{
				if(it.SuitOfCard == card.SuitOfCard)
				{
					int thisDistance = it.ValueOfCard - card.ValueOfCard;
					
					if(thisDistance < 0)
					{
						thisDistance = -thisDistance;
					}
					
					if(thisDistance < distance)
					{
						distance = thisDistance;
					}
				}
			}
			
			return distance;
		}
		
		#endregion
		
		#region overrides
		
		/// <summary>
		/// This method chooses the cards the AI wants to play.
		/// 
		/// This perticular AI selects cards based on how well the cards on
		/// the table fit together with other cards allready taken.
		/// </summary>
		/// <param name="player">A reference to the player making the move</param>
		public override void SelectCardsAccordingToPhase(Player player)
		{
			if(player.CurrentHand.Cards.Count > 0)
			{
				//alright, can I win this hand? or at least fight for it?
				int highestCardOnTable = 0;
				
				try
				{
					highestCardOnTable = PoomanRules.Instance.CurrentRound.GetHighestCardInPlay(null).ValueOfCard;
				}
				catch(Exception)
				{
				}
				
				int highestCardOnHand = HighestCardvalueInPile(player.CurrentHand.Cards);
				
				if(highestCardOnHand >= highestCardOnTable)
				{
					//ok, so I could win this stick.. do I have enough cards on hand yet to start looking for straights?
					if(player.HandPhase2.Cards.Count >= m_MinimumCardsWon)
					{
						//I have enough cards to check for straights, so let's do..
						//find the most important card with which I can take all the cards
						bool connected = false;
						
						foreach(CardPile itPile in m_GameTable.CardsOnTable)
						{
							foreach(Card itCard in itPile)
							{
								if(FoundConnection(itCard, player.HandPhase2.Cards))
								{
									connected = true;
								}
							}
						}
						
						foreach(Card it in player.CurrentHand.Cards)
						{
							if(FoundConnection(it, player.HandPhase2.Cards))
							{
								if(it.ValueOfCard >= highestCardOnTable)
								{
									connected = true;
								}
							}
						}
						
						if(connected)
						{
							//I have at least one connection.. let's see which of the cards on hand
							//can give most new connections
							int mostConnectionsYet = 0;
							Card mostConnectedCard = null;
							
							foreach(Card it in player.CurrentHand.Cards)
							{
								if(it.ValueOfCard >= highestCardOnTable)
								{
									CardPile testPile = new CardPile(player.HandPhase2.Cards);
									testPile.Add(it);
									int connections = ConnectedCards(testPile);
									
									if(connections >= mostConnectionsYet)
									{
										mostConnectedCard = it;
										mostConnectionsYet = connections;
									}
								}
							}
							
							CardPile tableHand2AndWinner = new CardPile(player.HandPhase2.Cards);
							CardPile tableAndWinner = new CardPile();
							
							tableHand2AndWinner.Add(mostConnectedCard);
							tableAndWinner.Add(mostConnectedCard);
							
							int numberOfCardsOnTable = 0;
							
							foreach(CardPile it in m_GameTable.CardsOnTable)
							{
								tableHand2AndWinner.Add(it);
								tableAndWinner.Add(it);
								numberOfCardsOnTable += it.Count;
							}
							
							tableHand2AndWinner.Sort();
							int connectionsBefore = ConnectedCards(player.HandPhase2.Cards);
							int connectionsAfter = ConnectedCards(tableHand2AndWinner);
							int aiSum = connectionsAfter - connectionsBefore;
							
							//iterate the table cards + the "winner to be"-card
							foreach(Card it in tableAndWinner)
							{
								//if one of these cards are not connected to any of the cards on the table,
								//to the winner cards, or to the cards allready on hand 2
								if(NotConnected(it, tableHand2AndWinner))
								{
									//then we want to know how far away this card is from the cards we allready have on hand 2
									aiSum -= Distance(it, player.HandPhase2.Cards);
								}
							}
							
							if(aiSum > -1)
							{
								//I want these cards, get them!
								//but what if we're not last in the swarm to play? then maybe we
								//should try and fight with a higher card?
								
								//TODO fight?
								mostConnectedCard.SelectCard(true);
							}
							else
							{
								if(numberOfCardsOnTable > 0)
								{
									//these cards are not worthy, so let's try and give them away
									Debug.WriteLine("Suit giving away cards, cause aiSum is " + aiSum + ".");
									Card giveAway = null;
									
									foreach(Card itCard in player.CurrentHand.Cards)
									{
										CardPile awayCards = new CardPile(player.HandPhase2.Cards);
										awayCards.Add(itCard);
										awayCards.Sort();
										
										if((NotConnected(itCard, awayCards)) &&
										   (itCard.ValueOfCard <= highestCardOnTable))
										{
											giveAway = itCard;
										}
									}
									
									if(giveAway == null)
									{
										Debug.WriteLine("Suit has to sacrifice good card");
										//oh no.. I have to give away a card that's valuable to me
										//find the least valuable
										
										//or maybe chance? if the cards are bad enough, it's worth a chance
										bool chanced = false;
										
										if(aiSum < m_ChanceLimit)
										{
											Debug.WriteLine("The cards are so bad.. There are now " + numberOfCardsOnTable + " cards on the table..");
											
											if(PoomanRules.Instance.MainPoo.ChanceCard(player))
											{
												chanced = true;
												Debug.WriteLine("Suit takes a chance..");
												Debug.WriteLine("");
												Debug.WriteLine("");
												Debug.Flush();
											}
										}
										else
										{
											Debug.WriteLine("Suit figures the sacrifice is too small to take a chance for..");
											Debug.WriteLine("");
											Debug.WriteLine("");
											Debug.Flush();
										}
										
										if(!chanced)
										{
											CardPile testPile = new CardPile(player.HandPhase2.Cards);
											testPile.Add(player.CurrentHand.Cards[0]);
											int fewestConnections = ConnectedCards(testPile);
											
											foreach(Card it in player.CurrentHand.Cards)
											{
												testPile = new CardPile(player.HandPhase2.Cards);
												testPile.Add(it);
												
												if(ConnectedCards(testPile) <= fewestConnections)
												{
													giveAway = it;
													fewestConnections = ConnectedCards(testPile);
												}
											}
											
											if(giveAway == null)
											{
												MessageBox.Show("Error.. This shouldn't happen, a giveAway-card was not found.", "AiGameStatePhase1Suit.SelectCardsAccordingToPhase line 203");
												giveAway = player.CurrentHand.Cards[0];
											}
											
											//right, I give you: the give away card.. :b
											giveAway.SelectCard(true);
										}
									}
									else
									{
										giveAway.SelectCard(true);
									}
								}
								else
								{
									//I'm first to act, let's simply play the most connected card!
									mostConnectedCard.SelectCard(true);
								}
							}
						}
						else
						{
							if(highestCardOnTable == 0)
							{
								//we're first to act, lets' play our most connected card, and hope we'll get it
								Debug.WriteLine("First to act?");
							}
							else
							{
								//right, we don't want these cards, let's try and shake'em
								Card lowest = LowestCardInPile(player.CurrentHand.Cards);
								//this operation may result in getting the cards, but at least we tried..
								//none of the cards are desirable, so it really doesn't matter what crappy
								//card we get along with the other crappy cards :[
								lowest.SelectCard(true);
							}
						}
					}
					else
					{
						//I still don't have enough cards on hand to start looking for stragihts
						//so let's just try and grab this hand, select the highest card, and any pairings
						foreach(Card it in player.CurrentHand.Cards)
						{
							if(it.ValueOfCard == highestCardOnHand)
							{
								it.SelectCard(true);
							}
						}
					}
				}
				else
				{
					//ok, I can't win these cards, so let's give them away with the least possible loss
					//also, with as high a card as possible, so we save the lower cards for the really tricky spots..
					
					//but what if they're reallly good? can I win them if I chance?
					//TODO let AI cheat, and look at the next card, and if it suits AI, use it
					
					CardPile firstTest = new CardPile(player.CurrentHand.Cards);
					Card leastImportant = player.CurrentHand.Cards[0];
					firstTest.Add(leastImportant);
					
					int connectionsAfter = ConnectedCards(firstTest);
					
					foreach(Card it in player.CurrentHand.Cards)
					{
						CardPile testCards = new CardPile(player.CurrentHand.Cards);
						testCards.Add(it);
						int connections = ConnectedCards(testCards);
						
						if((connections < connectionsAfter) ||
						   ((connections == connectionsAfter) && (it.ValueOfCard > leastImportant.ValueOfCard)))
						{
							leastImportant = it;
							connectionsAfter = connections;
						}
					}
					
					leastImportant.SelectCard(true);
				}
				
				//now, if no card is selected, then I'm badly coded :(
				if(player.CurrentHand.SelectedCards() == 0)
				{
					Debug.WriteLine("Suit registers no selected card.");
					Debug.Flush();
					
					if(player.CurrentHand.Cards.Count > 0)
					{
						player.CurrentHand.Cards[0].SelectCard(true);
					}
				}
				
				player.Controler.PressedPlay = true;
			}
			else
			{
				MessageBox.Show("Phase 1 seems to be over.. Last player was SuitAI");
				PoomanRules.Instance.MainPoo.SwitchPhase();
			}
		}
		
		/// <summary>
		/// Evaluates how good the taken cards are
		/// </summary>
		/// <param name="player">The player evaluating</param>
		/// <param name="cards">The cards to evaluate</param>
		/// <returns></returns>
		public override Labb4.Pooman.Players.Player.Evaluation EvaluateWonCardsPhase1(Player player, CardPile cards)
		{
			return player.EvaluateBasedOnSuit(cards);
		}
		
		#endregion
		
		#region private methods
		
		/// <summary>
		/// Finds all cards that are directly connected to another card, and counts these
		/// </summary>
		/// <param name="cards">The CardPile to look for connected cards in</param>
		/// <returns>The number of connected cards</returns>
		private int ConnectedCards(CardPile cards)
		{
			cards.Sort();
			int connected = 0;
			int lastCardValue = 0;
			bool lastWasConnected = false;
			
			if(cards.Count > 0)
			{
				lastCardValue = cards[0].ValueOfCard;
			}
			
			foreach(Card it in cards)
			{
				if(it.ValueOfCard == (lastCardValue + 1))
				{
					if(lastWasConnected)
					{
						connected += 1;
					}
					else
					{
						connected += 2;
					}
					
					lastWasConnected = true;
				}
				else
				{
					lastWasConnected = false;
				}
				
				lastCardValue = it.ValueOfCard;
			}
			
			return connected;
		}
		
		/// <summary>
		/// Establishes whether or not a Card is connected in straight (by suit and rank)
		/// in a CardPile. If the Card passed is not part of the CardPile passed, the
		/// result will be true.
		/// 
		/// You have to sort the CardPile before calling this method!
		/// </summary>
		/// <param name="card">The Card to evaluate</param>
		/// <param name="cards">The CardPile to look in</param>
		/// <returns>True if the card is NOT connected, false if connected</returns>
		private bool NotConnected(Card card, CardPile cards)
		{
			bool connected = false;
			
			if(cards.Contains(card))
			{
				for(int i = 0; i < cards.Count; i++)
				{
					if(cards[i] == card)
					{
						if(i > 0)
						{
							if(cards[i - 1].ValueOfCard == (card.ValueOfCard - 1))
							{
								connected = true;
							}
						}
						
						if(i < (cards.Count - 1))
						{
							if(cards[i + 1].ValueOfCard == (card.ValueOfCard + 1))
							{
								connected = true;
							}
						}
					}
				}
			}
			
			return !connected;
		}
		
		/// <summary>
		/// Determines if a card is connected to those in a cardpile. Whether or not the card
		/// is part of the cardpile makes no difference. The cardpile does not have to be sorted
		/// before calling this method
		/// </summary>
		/// <param name="card">The card to look for</param>
		/// <param name="cards">The cardpile to check</param>
		/// <returns></returns>
		private bool FoundConnection(Card card, CardPile cards)
		{
			bool connected = false;
			
			foreach(Card it in cards)
			{
				if((it.ValueOfCard == (card.ValueOfCard - 1)) ||
				   (it.ValueOfCard == (card.ValueOfCard + 1)))
				{
					connected = true;
				}
			}
			
			return connected;
		}
		
		#endregion
	}
}
